package com.fanghaoran.dao;

import com.fanghaoran.util.JdbcUtils;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: win11
 * @Date: 2023/03/14/19:17
 * @Description: 封装dao层的数据库重复代码
 * 封装两个方法
 * 1.非DQL操作
 * 2.DQL操作
 */
public abstract class BaseDao {
    /**
     * 封装简化非DQL语句
     * @param sql    带占位符的SQL语句
     * @param params 占位符的具体值
     * @return 执行影响的行数
     */
    public static int executeUpdate(String sql, Object... params) throws SQLException {
        //获取连接(调用工具类中的获取连接方法,这个方法在底层注册驱动,用连接池来创建连接)
        Connection connection = JdbcUtils.getConnection();

        //获取发送sql语句的对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //占位符赋值
        //可变参数本质是一个数组,遍历进行赋值
        if (params != null && params.length != 0) {
            for (int i = 1; i <= params.length; i++) {
                preparedStatement.setObject(i, params[i - 1]);
            }
        }

        //发送sql语句(DML类型)
        int rows = preparedStatement.executeUpdate();

        //释放资源
        preparedStatement.close();
        //对于是否回收连接,需要考虑是不是事务
        if (connection.getAutoCommit()) {
            //为true表明没有关闭默认方式 可以正常回收(用工具类中的回收方法)
            JdbcUtils.freeConnection();
        }

        return rows;
    }

    /**
     * 封装简化DQL语句
     *
     * @param clazz  类的class文件
     * @param sql    sql语句
     * @param params 占位符的具体值
     * @param <T>    泛型,放在返回值前面->表示这是一个泛型方法(不能确定的返回值类型)
     * @return 将对象放在装着实例类对象的集合中, 每个对象装着一行的数据, 属性跟数据中的列名一一对应
     */
    public static <T> List<T> executeQuery(Class<T> clazz, String sql, Object... params) throws Exception {
        //获取连接
        Connection connection = JdbcUtils.getConnection();
        //获取发送sql语句对象
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //占位符赋值
        if (params != null && params.length != 0) {
            for (int i = 1; i <= params.length; i++) {
                preparedStatement.setObject(i, params[i - 1]);
            }
        }
        //发送并获取结果集
        ResultSet resultSet = preparedStatement.executeQuery();

        // 4. 解析结果集,解析数据
        //获取当前结果集->列的信息(列名/别名,列的数量 获取列的数据是在resultSet中)
        ResultSetMetaData metaData = preparedStatement.getMetaData();
        //获取列的数量
        int columnCount = metaData.getColumnCount();
        //list集合存储整个结果集数据
        ArrayList<T> list = new ArrayList<>();
        //自动遍历行
        while (resultSet.next()) {
            //每一行数据对应一个T类型的对象
            //利用反射来获取这个未知类型的实例化对象
            T t = clazz.newInstance();

            //自动遍历列
            for (int i = 1; i <= columnCount; i++) {
                //获取特定行特定列处的数据
                Object value = resultSet.getObject(i);
                //获取列的名字/别名
                //getColumnLabel方法优先获取别名,没有再获取列名
                //getColumnClassName只获取原本的列名,不会获取别名
                String columnLabel = metaData.getColumnLabel(i);

                //利用反射拿到对象的属性并赋值
                //参数是属性的名字
                Field field = clazz.getDeclaredField(columnLabel);
                //开启访问权限,打破private修饰符的限制
                field.setAccessible(true);
                //参数一:要对哪个实例化对象的该属性赋值
                //参数二:要赋的值
                field.set(t, value);
            }
            //添加一行一行的数据
            list.add(t);
        }

        // 5. 释放资源
        resultSet.close();
        preparedStatement.close();
        //判断连接是否该回收/关闭
        if (connection.getAutoCommit()) {
            JdbcUtils.freeConnection();
        }
        return list;
    }
}
